
#include "Cameras.h"
#include "PointTransformations.h"

using namespace std;
using namespace cagd;


CCamera::CCamera(ECameraType cameraType, const DCoordinate &right, const DCoordinate &up, const DCoordinate &look, const DCoordinate &position):
_cameraType(cameraType),
_right(right),
_up(up),
_look(look),
_position(position)
{
}


void CCamera::GetModelViewMatrix(vector<float> &matrix)
{
	// size of matrix must be 16
	matrix.resize(16);

	//----------------------
	// ensure that:
	// + axes of the new coordinate system are pairwise orthogonal
	// + the vectors along the axes have unit length
	// + the coordinate system is right handed
	//----------------------
	_look.Normalize();

	_up = _right % _look;
	_up.Normalize();

	_right = _look % _up;
	_right.Normalize();

	//----------------------
	// build the view matrix
	//----------------------
	// first column
	matrix[0] = _right.x;
	matrix[1] = _up.x;
	matrix[2] = _look.x;
	matrix[3] = 0.0f;

	// second column
	matrix[4] = _right.y;
	matrix[5] = _up.y;
	matrix[6] = _look.y;
	matrix[7] = 0.0f;

	// third column
	matrix[ 8] = _right.z;
	matrix[ 9] = _up.z;
	matrix[10] = _look.z;
	matrix[11] = 0.0f;

	// fourth column
	matrix[12] = -(_right * _position);
	matrix[13] = -(_up * _position);
	matrix[14] = -(_look * _position);
	matrix[15] = 1.0f;
}

void CCamera::GetModelViewMatrix(CTransformation &transformation)
{
	//----------------------
	// ensure that:
	// + axes of the new coordinate system are pairwise orthogonal
	// + the vectors along the axes have unit length
	// + the coordinate system is right handed
	//----------------------
	_look.Normalize();

	_up = _right % _look;
	_up.Normalize();

	_right = _look % _up;
	_right.Normalize();

	//----------------------
	// build the view matrix
	//----------------------
	// first column
	transformation.matrix[0] = _right.x;
	transformation.matrix[1] = _up.x;
	transformation.matrix[2] = _look.x;
	transformation.matrix[3] = 0.0f;

	// second column
	transformation.matrix[4] = _right.y;
	transformation.matrix[5] = _up.y;
	transformation.matrix[6] = _look.y;
	transformation.matrix[7] = 0.0f;

	// third column
	transformation.matrix[ 8] = _right.z;
	transformation.matrix[ 9] = _up.z;
	transformation.matrix[10] = _look.z;
	transformation.matrix[11] = 0.0f;

	// fourth column
	transformation.matrix[12] = -(_right * _position);
	transformation.matrix[13] = -(_up * _position);
	transformation.matrix[14] = -(_look * _position);
	transformation.matrix[15] = 1.0f;

}


void CCamera::Pitch(float angle)
{
	CRotate R(_right, angle);
	_up   = R * _up;
	_look = R * _look;
}

void CCamera::Yaw(float angle)
{
	CTransformation T;

        DCoordinate helper(0.0, 1.0, 0.0);
	switch (_cameraType)
	{
        case LANDOBJECT:
                T = CRotate(helper, angle);
		break;
	case AIRCRAFT:
		T = CRotate(_up, angle);
		break;
	}

	_right	= T * _right;
	_look	= T * _look;
}

void CCamera::Roll(float angle)
{
	if (_cameraType == AIRCRAFT)
	{
		CRotate R(_look, angle);
		_right	= R * _right;
		_up		= R * _up;
	}
}

void CCamera::Walk(float units)
{
	switch (_cameraType)
	{
	case LANDOBJECT:		// move only in xz plane for land object
                _position += DCoordinate(_look.x, 0.0, _look.z) * units;
		break;
	case AIRCRAFT:
		_position += _look * units;
		break;
	}
}

void CCamera::Strafe(float units)
{
	switch (_cameraType)
	{
	case LANDOBJECT:		// move only in xz plane for land object
                _position += DCoordinate(_right.x, 0.0, _right.z) * units;
		break;
	case AIRCRAFT:
		_position += _right * units;
		break;
	}
}

void CCamera::Heighten(float units)
{
	if (_cameraType == AIRCRAFT)
		_position += _up * units;
}

void CCamera::SetCameraType(ECameraType cameraType)
{
	_cameraType = cameraType;
}

DCoordinate CCamera::GetPosition() const
{
	return _position;
}

void CCamera::SetPosition(const DCoordinate &position)
{
	_position = position;
}

DCoordinate CCamera::GetRight() const
{
	return _right;
}

void CCamera::SetRight(const DCoordinate &right)
{
	_right = right;
	_right.Normalize();
}

DCoordinate CCamera::GetUp() const
{
	return _up;
}

void CCamera::SetUp(const DCoordinate &up)
{
	_up = up;
	_up.Normalize();
}

DCoordinate CCamera::GetLook() const
{
	return _look;
}

void CCamera::SetLook(const DCoordinate &look)
{
	_look = look;
	_look.Normalize();
}
